Picnic Planning
题意
矮人虽小却喜欢乘坐巨大的轿车,车大到能够装下不管多少矮人。某天,N(N≤20)个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A 要么开车到矮人B 家中,留下自己的轿车在矮人B 家,然后乘坐B 的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。尽管矮人的家非常大,能够停放无数量轿车,可是聚餐地点却最多仅仅能停放K 辆轿车。给你一张加权无向图,描写叙述了N 个矮人的家和聚餐地点,求出全部矮人开车最短总路程
题解
题本身是一道$k$限制生成树裸题,但那东西太迷幻,不会.因为数据规模比较小,暴力能过,所以也没学.这里用的是状压$+Prim$.
因为根节点只能连不大于$k$条边,所以我们暴力枚举根节点能连的边的状态,再将剩下的点跑最小生成树,更新最小代价.
- 用$map$映射每个人的名字
- 可能有重边,需要更新两点之间的最短距离
- 这里我们用邻接矩阵存边,用$Prim$跑最小生成树,不然时间复杂度过不去
- 每次生成树必须加够$N-1$条边后才能更新$ans$
- 输出应遵循题目规则
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=430;
int n,k,num,cnt;
int fa[22],ans,bns;
map< string , int >mm;
int ss[22][22];
struct fy
{
int from,to,d;
bool operator<(const fy&a)const{return d<a.d;};
}q[maxn];
char str[30],str1[30];
void add(int a,int b,int c){q[++num]=(fy){a,b,c};}
int can(int a)
{
bns=0;
int he=0,w=1;
while(a)
{
++w;
if(a&1)
{
he++;fa[w]=1;
if(ss[1][w]==ss[0][0])return 0;
bns+=ss[1][w];
}
a>>=1;
}
if(he<=k)return he;
return 0;
}
int find(int a)
{
while(a!=fa[a])a=fa[a]=fa[fa[a]];
return a;
}
void ku(int x)//披着羊皮的狼(滑稽)
{
if(!x)return;
for(int i=1;i<=num;i++)
{
if(q[i].from==1||q[i].to==1)continue;
int a=find(q[i].from);
int b=find(q[i].to);
if(a!=b)
{
if(x==(cnt-1))break;
fa[a]=b;
x++;
bns+=q[i].d;
if(bns>ans)return;
}
}
if(x==(cnt-1))ans=min(ans,bns);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
mm.clear();cnt=0;num=0;
scanf("%d",&n);memset(ss,0x3f,sizeof ss);
mm["Park"]=++cnt;ans=ss[0][0];
for(int i=1;i<=n;i++)
{
int a,b,c;
scanf("%s%s%d",str,str1,&c);
if(!mm[str])mm[str]=++cnt;
if(!mm[str1])mm[str1]=++cnt;
a=mm[str];b=mm[str1];
ss[a][b]=min(ss[a][b],c);
ss[b][a]=ss[a][b];
}
scanf("%d",&k);
for(int i=1;i<=cnt;i++)for(int j=i+1;j<=cnt;j++)
if(ss[i][j]&&(ss[i][j]!=ss[0][0]))add(i,j,ss[i][j]);
sort(q+1,q+1+num);
for(int s=0;s<(1<<(cnt-1));s++)//枚举根节点的状态
{
for(int i=1;i<=cnt;i++)fa[i]=i;
ku(can(s));
}
printf("Total miles driven: %d\n",ans);//输出
}
return 0;
}